package top.lyjwn.todo.weinxin.mch.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import top.lyjwn.todo.aop.log.controller.ControllerLog;
import top.lyjwn.todo.aop.token.ExemptToken;
import top.lyjwn.todo.common.component.InfLogHandle;
import top.lyjwn.todo.common.model.LogE;
import top.lyjwn.todo.common.template.R;
import top.lyjwn.todo.common.utils.AESUtil;
import top.lyjwn.todo.common.utils.WXNotify;
import top.lyjwn.todo.common.utils.WXSignUtil;
import top.lyjwn.todo.common.utils.XMLUtil;
import top.lyjwn.todo.weinxin.data.mch.PayResultNotice;
import top.lyjwn.todo.weinxin.data.mch.RefundResultNotice;
import top.lyjwn.todo.weinxin.data.mch.RefundResultNoticeReqInfo;
import top.lyjwn.todo.weinxin.data.mch.ReturnResult;
import top.lyjwn.todo.weinxin.mch.service.WXPayNotarize;
import top.lyjwn.todo.weinxin.param.WXPayParam;

import jakarta.servlet.http.HttpServletRequest;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import java.util.concurrent.*;

/**
 * 微信支付控制器
 *
 * @author luoyujie
 */
@RestController
@RequestMapping(PayController.CONTROLLER_NAME)
public class PayController {


    /**
     * 日志队列
     */
    private final static ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

    @Autowired(required = false)
    private InfLogHandle infLogHandle;

    private final Logger log = LoggerFactory.getLogger(getClass());

    @Autowired(required = false)
    private WXPayNotarize wxNotarize;

    @Autowired
    private WXPayParam wxPayParam;

    /**
     * 支付控制器名称
     */
    public final static String CONTROLLER_NAME = "/pay";

    /**
     * 支付回调，支付通知路径
     */
    public final static String PAY_NOTIFY_PATH = "/jsapi/pay";

    /**
     * 退款回调，退款通知路径
     */
    public final static String REFUND_NOTIFY_PATH = "/jsapi/refund";


    /**
     * 支付通知，回调接口
     *
     * @param request
     */
    @ControllerLog(description = "支付通知", isStandard = false)
    @ExemptToken
    @RequestMapping(value = PAY_NOTIFY_PATH, method = RequestMethod.POST)
    public String notarizePay(HttpServletRequest request) {
        String xmlStr = WXNotify.getString(request);
        LogE logE = new LogE();
        logE.setRequestTime(new Date());
        logE.setInfNme("notarizePay");
        logE.setInputParam(xmlStr);
        ReturnResult returnResult = new ReturnResult();
        PayResultNotice payResultNotice = XMLUtil.convertXmlStrToObject(PayResultNotice.class,
                xmlStr);
        payResultNotice = payResultNotice.replenish();
        if (payResultNotice == null) {
            log.error("解析【支付通知】参数为null，通知报文为:" + xmlStr);
            returnResult.setReturn_code("FAIL");
            returnResult.setReturn_msg("签名为空");
            String msg = XMLUtil.convertToXml(returnResult);
            logE.setOutputParam(msg);
            logE.setResponseTime(new Date());
            if (infLogHandle != null) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        infLogHandle.write(logE);
                    }
                });
            }
            return msg;
        }
        logE.setKeyWord(payResultNotice.getOut_trade_no() + "," + payResultNotice.getAttach() + "," + payResultNotice.getTransaction_id());
        String sign = payResultNotice.getSign();
        if (sign == null) {
            returnResult.setReturn_code("FAIL");
            returnResult.setReturn_msg("签名为空");
            String msg = XMLUtil.convertToXml(returnResult);
            logE.setOutputParam(msg);
            logE.setResponseTime(new Date());
            if (infLogHandle != null) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        infLogHandle.write(logE);
                    }
                });
            }
            return msg;
        }
        payResultNotice.setSign(null);
        if (!WXSignUtil.verify(wxPayParam.getApiKey(), xmlStr, sign)) {
            returnResult.setReturn_code("FAIL");
            returnResult.setReturn_msg("签名验证失败");
            String msg = XMLUtil.convertToXml(returnResult);
            logE.setOutputParam(msg);
            logE.setResponseTime(new Date());
            if (infLogHandle != null) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        infLogHandle.write(logE);
                    }
                });
            }
            return msg;
        }
        if (wxNotarize != null) {
            R r;
            try {
                r = wxNotarize.notarizePay(payResultNotice);
//            } catch (org.springframework.orm.ObjectOptimisticLockingFailureException ex) {
            } catch (RuntimeException ex) {
                StringWriter sw = new StringWriter();
                PrintWriter pw = new PrintWriter(sw);
                // 将出错的栈信息输出到printWriter中
                ex.printStackTrace(pw);
                pw.flush();
                sw.flush();
                log.error("notarizePay:===>回调his时发生:" + sw.toString());
                //TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                String msg = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[You're doing it too often " + ex.getClass().getName() + " ]]></return_msg></xml>";
                logE.setOutputParam(msg);
                logE.setResponseTime(new Date());
                if (infLogHandle != null) {
                    executor.execute(new Runnable() {
                        @Override
                        public void run() {
                            infLogHandle.write(logE);
                        }
                    });
                }
                return msg;
            }

            if (r.isDone()) {
                String msg = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
                logE.setOutputParam(msg);
                logE.setResponseTime(new Date());
                if (infLogHandle != null) {
                    executor.execute(new Runnable() {
                        @Override
                        public void run() {
                            infLogHandle.write(logE);
                        }
                    });
                }
                return msg;
            } else {
                returnResult.setReturn_code("FAIL");
                returnResult.setReturn_msg(r.getMsg());
                String msg = XMLUtil.convertToXml(returnResult);
                logE.setOutputParam(msg);
                logE.setResponseTime(new Date());
                if (infLogHandle != null) {
                    executor.execute(new Runnable() {
                        @Override
                        public void run() {
                            infLogHandle.write(logE);
                        }
                    });
                }
                return msg;
            }
        }
        String msg = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[I didn't handle it successfully]]></return_msg></xml>";
        logE.setOutputParam(msg);
        logE.setResponseTime(new Date());
        if (infLogHandle != null) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    infLogHandle.write(logE);
                }
            });
        }
        return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[I didn't handle it successfully]]></return_msg></xml>";
    }


    /**
     * 退款通知，回调接口
     *
     * @param request
     */
    @ControllerLog(description = "退款通知", isStandard = false)
    @ExemptToken
    @RequestMapping(value = REFUND_NOTIFY_PATH, method = RequestMethod.POST)
    public String notarizeRefund(HttpServletRequest request) {
        String xmlStr = WXNotify.getString(request);
        LogE logE = new LogE();
        Date date = new Date();
        logE.setRequestTime(date);
        logE.setInfNme("notarizeRefund");
        logE.setInputParam(xmlStr);
        if (infLogHandle != null) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    LogE logE = new LogE();
                    logE.setRequestTime(date);
                    logE.setInfNme("notarizeRefundOrigin");
                    logE.setInputParam(xmlStr);
                    infLogHandle.write(logE);
                }
            });
        }
        RefundResultNotice refundResultNotice = XMLUtil
                .convertXmlStrToObject(RefundResultNotice.class, xmlStr);
        if (refundResultNotice == null) {
            ReturnResult returnResult = new ReturnResult();
            returnResult.setReturn_code("FAIL");
            returnResult.setReturn_msg("解析【退费通知】参数为null，通知报文为" + xmlStr);
            String msg = XMLUtil.convertToXml(returnResult);
            logE.setOutputParam(msg);
            logE.setResponseTime(new Date());
            if (infLogHandle != null) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        infLogHandle.write(logE);
                    }
                });
            }
            return msg;
        }
        String req_info = refundResultNotice.getReq_info();
        ReturnResult returnResult = new ReturnResult();
        String decryptData = "";
        try {
            decryptData = AESUtil.decryptData(req_info, wxPayParam.getApiKey());
        } catch (Exception e) {
            returnResult.setReturn_code("FAIL");
            returnResult.setReturn_msg(e.getMessage());
            String msg = XMLUtil.convertToXml(returnResult);
            logE.setOutputParam(msg);
            logE.setResponseTime(new Date());
            if (infLogHandle != null) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        infLogHandle.write(logE);
                    }
                });
            }
            return msg;
        }
        RefundResultNoticeReqInfo refundResultNoticeReqInfo = XMLUtil
                .convertXmlStrToObject(RefundResultNoticeReqInfo.class, decryptData);
        //解密成功后写入解密的入参
        logE.setKeyWord(refundResultNoticeReqInfo.getOut_trade_no() + "," + refundResultNoticeReqInfo.getTransaction_id() + "," + refundResultNoticeReqInfo.getRefund_id());
        logE.setInputParam(decryptData);
        if (refundResultNoticeReqInfo == null) {
            log.error("解析退款通知参数为null，通知报文为:" + xmlStr);
            returnResult.setReturn_code("FAIL");
            returnResult.setReturn_msg("解析退款通知参数为null，通知报文为:" + xmlStr);
            String msg = XMLUtil.convertToXml(returnResult);
            logE.setOutputParam(msg);
            logE.setResponseTime(new Date());
            if (infLogHandle != null) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        infLogHandle.write(logE);
                    }
                });
            }
            return msg;
        }
        if (wxNotarize != null) {
            R r;
            try {
                r = wxNotarize.notarizeRefund(refundResultNoticeReqInfo);
//            } catch (org.springframework.orm.ObjectOptimisticLockingFailureException ex) {
            } catch (RuntimeException ex) {
                StringWriter sw = new StringWriter();
                PrintWriter pw = new PrintWriter(sw);
                // 将出错的栈信息输出到printWriter中
                ex.printStackTrace(pw);
                pw.flush();
                sw.flush();
                log.error("notarizeRefund:===>回调his时发生:" + sw.toString());
                //TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                String msg = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[You're doing it too often " + ex.getClass().getName() + "]]></return_msg></xml>";
                logE.setOutputParam(msg);
                logE.setResponseTime(new Date());
                if (infLogHandle != null) {
                    executor.execute(new Runnable() {
                        @Override
                        public void run() {
                            infLogHandle.write(logE);
                        }
                    });
                }
                return msg;
            }
            if (r.isDone()) {

                String msg = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
                logE.setOutputParam(msg);
                logE.setResponseTime(new Date());
                if (infLogHandle != null) {
                    executor.execute(new Runnable() {
                        @Override
                        public void run() {
                            infLogHandle.write(logE);
                        }
                    });
                }
                return msg;
            } else {
                returnResult.setReturn_code("FAIL");
                returnResult.setReturn_msg(r.getMsg());
                String msg = XMLUtil.convertToXml(returnResult);
                logE.setOutputParam(msg);
                logE.setResponseTime(new Date());
                if (infLogHandle != null) {
                    executor.execute(new Runnable() {
                        @Override
                        public void run() {
                            infLogHandle.write(logE);
                        }
                    });
                }
                return msg;
            }
        } else {
            String msg = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[I didn't handle it successfully]]></return_msg></xml>";
            logE.setOutputParam(msg);
            logE.setResponseTime(new Date());
            if (infLogHandle != null) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        infLogHandle.write(logE);
                    }
                });
            }
            return msg;
        }
    }
}
